package factory;

import bean.BeanDefinition;
import exception.BeanCurrentlyInCreationException;

import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class AbstractBeanFactory implements BeanFactory {

    /**
     * Map<beanId, BeanDefinition>
     */
    Map<String, BeanDefinition> beanDefinitionMap;

    /**
     * 真正的存储bean容器
     *   Map<beanId, Bean>
     */
    private Map<String, Object> beanMap = new HashMap<>();

    /**
     * 存放单例名称集合 Set<beanId> 用来判断是否创建单例
     */
    private final Set<String> singletonsCurrentlyInCreation =
            Collections.newSetFromMap(new ConcurrentHashMap<>(16));


    public AbstractBeanFactory() {
        beanDefinitionMap = new HashMap();
    }

    @Override
    public void registerBeanDefinition(String className, BeanDefinition beanDefinition) {
        beanDefinitionMap.put(className, beanDefinition);
    }

    @Override
    public void cacheBean(String className, Object object) {
        beanMap.put(className, object);
    }

    @Override
    public Object getBean(String beanName) {
        Object beanInstance = beanMap.get(beanName);
        if (beanInstance == null) {
            if (singletonsCurrentlyInCreation.contains(beanName)) {
                // 单例重复创建
                throw new BeanCurrentlyInCreationException(beanName);
            }
            singletonsCurrentlyInCreation.add(beanName);

            Object bean = null;
            try {
                // 创建真正的bean
                bean = beanDefinitionMap.get(beanName).getBean(this);
            } catch (IllegalAccessException | InstantiationException | NoSuchMethodException | ClassNotFoundException | InvocationTargetException e) {
                e.printStackTrace();
            }

            // ??? 为何又从中移除出来了
            singletonsCurrentlyInCreation.remove(beanName);
            return bean;
        }
        return beanInstance;
    }

    @Override
    public String getBeanType(String beanId) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanId);
        return beanDefinition.getBeanClassName();
    }

    @Override
    public Object getObjectFromFactoryBean(FactoryBean factoryBean) {
        try {
            return factoryBean.getObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


}
